//alu.c

/*
 * Team Name: Focus
 * Team Members: Diana Groun, Edward Bassan
 * File Name; alu.c
 * Description: This file contains the methods that the alu operation needs.
 * Methods such as subtraction addition ect.
 */

#include "alu.h"
#include "global.h"
#include <stdlib.h>
#include <stdio.h>

/*
 * A AluPtr Constructor.
 */
AluPtr aluConstructor(void) {
	AluPtr alu = (AluPtr) malloc(sizeof(AluStr));
	alu->operandA = malloc(sizeof(Register));
	alu->operandB = malloc(sizeof(Register));
	alu->resultR = malloc(sizeof(Register));
	alu->modifier = 0;
	alu->negFlag = 0;
	alu->zeroFlag = 0;
	alu->posFlag = 0;
	alu->carryFlag = 0;
	alu->overflowFlag = 0;

	//Pointers to functions.
	alu->signOf = signOf;
	alu->xor = xor;
	alu->division = division;
	alu->multiplication = multiplication;
	alu->negation = negation;
	alu->addition = addition;
	alu->subtraction = subtraction;
	alu->and = and;
	alu->or = or;
	alu->not = not;
	alu->isUnary = isUnary;
	alu->getResult = getResult;
	alu->setFlags = setFlags;
	alu->displayAlu = displayAlu;
	alu->shiftLeft = shiftLeft;
	alu->shiftRight = shiftRight;
	return alu;
}
// Check the sign of the operand.
int signOf(AluPtr this, unsigned short operand)
{
	return ((operand & SIGN_MASK) ? 1 : 0);
}

/*
 * Shifts register value to left.
 * reg - the value to be shifted
 */
int shiftLeft(AluPtr this)
{
	this->resultR->value = this->operandA->value * 2;
	this->setFlags(this);
	return NO_ERROR;
}
/**
 * Shifts the register value to the right.
 * reg - the value to be shifted.
 */
int shiftRight(AluPtr this)
{
	this->resultR->value = this->operandA->value / 2;
	this->setFlags(this);
	return NO_ERROR;
}
/**
 * Subtract the operandB from operandA and sets flags.
 */
int subtraction(AluPtr this)
{
	this->resultR->value = this->operandA->value - this->operandB->value;
	this->setFlags(this);
	return NO_ERROR;
}
/**
 * Returns the result.
 */
unsigned short getResult(AluPtr this)
{
	return this->resultR->value;
}
/**
 * Negate a register value.
 */
int negation(AluPtr this)
{
	this->operandB->value = ~this->operandB->value;
	return NO_ERROR;
}
/**
 * And the operandA value with operandB
 */
int and(AluPtr this)
{
	this->resultR->value = this->operandA->value & this->operandB->value;
	return NO_ERROR;
}
/**
 * Not operandA value.
 */
int not(AluPtr this)
{
	return this->resultR->value = ~(this->operandA->value);
}
/**
 * Or operandA value with operandB value.
 */
int or(AluPtr this)
{
	this->resultR->value = this->operandA->value | this->operandB->value;
	return NO_ERROR;
}
/*
 * XOR operandA value with operandB value.
 */
int xor(AluPtr this)
{
	this->resultR->value = this->operandA->value ^ this->operandB->value;
	return NO_ERROR;
}
/**
 * Multiply the low order byte of operandA value with the low order byte of operandB value.
 */
int multiplication(AluPtr this)
{
	RegisterPtr a = newRegister();
	RegisterPtr b = newRegister();
	a->setRegister(a, this->operandA->value);
	b->setRegister(b, this->operandB->value);
	this->resultR->value = a->getLOB(a) * b->getLOB(b);
	return NO_ERROR;
}
/**
 * Divide operandA value with operandB value and save the remainder to register # 8.
 */
int division(AluPtr this,RegisterPtr remain)
{
	this->resultR->value = this->operandA->value / this->operandB->value;
	remain->value= this->operandA->value % this->operandB->value;

	return NO_ERROR;
}
/**
 * Add the operandA value with operandB value.
 */
int addition(AluPtr this)
{
	this->resultR->value = this->operandA->value + this->operandB->value;
	this->setFlags(this);
	return NO_ERROR;
}
/**
 * sets the flags for when the result is zero, positive, negative, carry and overflow.
 */
int setFlags(AluPtr this) {
	if (this->signOf(this, this->resultR->value)){
		this->negFlag = 1;
	}

	if (this->resultR->value == 0x00){
		this->zeroFlag = 0x1;
	}
	if (this->resultR->value < (this->operandA->value + this->operandB->value))
		this->carryFlag = 1;

	if (this->signOf(this, this->operandA->value) == this->signOf(this, this->operandB->value)){
		if(this->signOf(this, this->operandA->value) != this->signOf(this, this->resultR->value)){
		this->overflowFlag = 1;
		}
	}
	return NO_ERROR;
}
/**
 * NOT an operandA value.
 */
int isUnary(int modifier)
{
	return NO_ERROR;
}
/**
 * Display the values for operands,result and the flags.
 */
int displayAlu(AluPtr this)
{
	unsigned short A,B,R;
	A = this->operandA->value;
	B = this->operandB->value;
	R = this->resultR->value;
	printf("ALU.A: %.4X\n",A);
	printf("ALU.B: %.4X\n",B);
	printf("ALU.R: %.4X\n",R);
	printf("SW   : %X%X%X%X\n",this->negFlag,this->zeroFlag,this->carryFlag,
			this->overflowFlag);
	return NO_ERROR;
}
/**
 * Perform operation based on the opcode.
 * this - the AluPtr
 */
int aluOperation(AluPtr this, int modifier)
{
	this->modifier = modifier;
	RegisterPtr remainder;
	switch (modifier)
	{
	case 0:
		this->addition(this);
		break;
	case 1:
		this->subtraction(this);
		break;
	case 2:
		this->multiplication(this);
		break;
	case 3:
		this->division(this, remainder);
		break;
	case 4:
		this->and(this);
		break;
	case 5:
		this->or(this);
		break;
	case 6:
		this->xor(this);
		break;
	case 7:
		this->not(this);
		break;
	case 8:
		this->shiftLeft(this);
		break;
	case 9:
		this->shiftRight(this);
		break;
	default:
		printf("Invalid modifier\n");
		break;
	}
	return NO_ERROR;
}
/**
 * Test for the Alu.c
 */
/*
int main(void)
{
        AluPtr alu = aluConstructor();
        alu->operandA->value = 0x2;
        alu->operandB->value = 0x2;
        alu->aluOperation(alu, ADD);
        alu->displayAlu(alu);
        return 0;
}
 */
